Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dogfood): add module #4

Merged
merged 48 commits into from
Mar 6, 2024
Merged

feat(dogfood): add module #4

merged 48 commits into from
Mar 6, 2024

Conversation

MaxMustermann2
Copy link
Contributor

Dogfooding is the practice of using one's own products or services. The module allows assets deposited on a client chain to be used to secure the Exocore chain.

  • The module uses its parameters to determine the epoch unit (can be hourly, daily or weekly) and the number of such epochs to use for the unbonding period. Besides these two main parameters, the other two parameters are the maximum number of validators and the number of historical entries (required for IBC compatibility).
  • Any delegation / undelegation changes and operator opt in and opt outs flow to the consensus engine (through corresponding module hooks) via the ABCI at the end of the epoch in which they are made. This, obviously, requires that the initial set of validators be provided in the genesis.json file using a trust-minimized (verifiable) approach which is done via the bootstrapping contract.
  • The unbonding time is calculated as the number of unbonding epochs * duration of an epoch after this time.
  • The module is designed to act as a drop-in replacement for the Cosmos SDK's staking module, and implements most of the interfaces required by other modules in the file impl_sdk.go.

Note that this module is not responsible for distributing rewards to the operators and delegators; it will have to be created separately.

Supersedes ExocoreNetwork/exocore-private#114 from the older repo.

The dogfooding module, as previously described, is used as the staking
module for the Exocore chain. It allows operators and delegators to
deposit and delegate their assets on a client chain for staking. The
staked assets are then used to secure the Exocore chain.

The parameters used by this module are the following:
 - EpochsUntilUnbonded, which represents the number of epochs after
   which an unbonding is effective.
 - EpochIdentifier, which is the identifier of the epoch duration. It
   should be valid according to the epoch keeper of `app.go`, and is
   thus constrained to the options week/day/hour.
 - MaxValidators, which is the maximum number of validators that will be
   forwarded to ABCI.
 - HistoricalEntries, which is the number of historical entries to
   persist in state. These are used by IBC.
The bootstrapping smart contract is responsible for accepting user
deposits, operator registration, and delegations. These operations may
flow freely until 24 hours (say) before the spawn time of Exocore, at
which the contract will be locked. During this time, the genesis state
of Exocore will be amended to record the initial deposits, delegations,
operator registrations and public keys. The last of these will feed into
the dogfooding module which will mark these operators as part of the
initial validator set and allow block production.

This PR includes the capability to load that state, and save the newly
created operator information to disk. In addition, the staking hooks
have been set up (partially) in this PR, which allow the SDK's slashing
keeper to record validator signing rates for downtime slashing.
The operator module should restrict an operator from doing things that
are logically not possible. For example, an operator that is already
opted in should not be able to opt in again. Similarly, an operator who
has opted out of a chain should not be able to replace the key for that
chain without opting back in. Based on these assumptions, the comment
maps out the list of possible operations that can happen within an epoch
and is used to ensure that all of the cases result in the unbonding
period being enforced accurately.
Whenever a delegation or undelegation event happens in the delegation
module, the hooks are called. For both of these events, as long as the
operator is not in the process of opting out from the chain, consensus
key operations are queued to be applied at the end of the current epoch.

For undelegation, specifically, the `recordKey` identifier of the
undelegation is used to mark within the delegation module that the
undelegation should be held until released at some point in the future
(as opposed to releasing it after a fixed number of blocks, which is the
default behaviour). This point in the future is calculated as the
earlier of the unbonding period end, or the operator's opt out period
end (if the operator is opting out).
The epochs hooks are used by the dogfood module to subscribe to the
start and end times of epochs. After an epoch ends, the operations that
are currently in the queue are upgraded to "pending", which are applied
at the end of the block. The upgrades are also made to undelegation
maturity, operator opt out, and operator key replacement data. Once the
pending actions are applied, they are cleared from the queue.

This branch is merged into dogfood-part5, which forms the basis of #121.
The validator set is not bound to change at each block. It can only
change at the end of an epoch, or following a slashing event. Therefore,
storing the historical information for each block is redundant. Instead,
we create a validator set id corresponding to each validator set, and
map each height to a validator set id. The mapping and the validator set
id are pruned once the historical info for a height is not required.

TODO: actually store the validator set against each id, within the
`EndBlock` function at the end of the epoch.
The delegation module calls the records as undelegation records, and
within itself stores the identifier for the record as the record key.
The dogfood module should follow the same naming convention and call
them undelegation record keys for clarity.
...and refactor getters / setters to `pending.go`.
* feat(dogfood): implement sdk staking interface

The interface required by IBC was already implemented in
`validators.go`. This PR takes that a step further and implements the
interfaces required to use the dogfood module as a drop-in replacement
for the SDK's staking module. The interfaces implemented are those
expected by the slashing, evidence and `genutil` modules. An explanation
has been provided above each function to explain why and where it is
called, and if its implementation is really necessary. There are still
some TODOs left in this PR that depend on the overall slashing /
operator design.

This branch is merged into dogfood-part6, which forms the basis of #122.

* fix(dogfood): store val set id + val set

at genesis, the val set id starts with 1. each time the validator set
changes, the val set id of the current block is retrieved. for the next
block, the val set is is stored as this id + 1.

* fix(dogfood): increment val set id correctly

in the case of genesis, we should not use height + 1 as the key in the
mapping; rather, it should be height. in all other cases, the mapping
key is height + 1. the value is the val set id in all cases.
feat(dogfood): implement epochs hooks + end block
feat(dogfood): implement delegation hooks
feat(dogfood): implement operator hooks
feat(dogfood): load genesis state for bootstrap
x/dogfood/types/keys.go Fixed Show fixed Hide fixed
x/dogfood/types/keys.go Fixed Show fixed Hide fixed
x/dogfood/types/keys.go Fixed Show fixed Hide fixed
x/dogfood/types/keys.go Fixed Show fixed Hide fixed
x/dogfood/types/keys.go Fixed Show fixed Hide fixed
x/dogfood/keeper/validators.go Fixed Show fixed Hide fixed
x/dogfood/keeper/unbonding.go Fixed Show fixed Hide fixed
x/dogfood/keeper/opt_out.go Fixed Show fixed Hide fixed
x/dogfood/keeper/opt_out.go Fixed Show fixed Hide fixed
x/dogfood/types/params.go Fixed Show fixed Hide fixed
x/dogfood/keeper/abci.go Fixed Show fixed Hide fixed
x/dogfood/keeper/abci.go Fixed Show fixed Hide fixed
x/dogfood/keeper/pending.go Fixed Show fixed Hide fixed
x/dogfood/keeper/pending.go Fixed Show fixed Hide fixed
x/dogfood/keeper/pending.go Fixed Show fixed Hide fixed
x/dogfood/keeper/abci.go Fixed Show fixed Hide fixed
x/dogfood/keeper/abci.go Fixed Show fixed Hide fixed
x/dogfood/keeper/abci.go Fixed Show fixed Hide fixed
x/dogfood/keeper/abci.go Fixed Show fixed Hide fixed
x/dogfood/module.go Fixed Show fixed Hide fixed
x/dogfood/keeper/validators.go Fixed Show fixed Hide fixed
x/dogfood/keeper/validators.go Fixed Show fixed Hide fixed
x/dogfood/keeper/validators.go Fixed Show fixed Hide fixed
x/dogfood/keeper/validators.go Fixed Show fixed Hide fixed
The `// #nosec G703` line needs to be placed above the multi-line
statement that it is ignoring, not in the middle of the statement which
contains the part to ignore.
* feat(dogfood): add asset_ids param

* feat(dogfood): move to average pricing

* fix(dogfood): relint, add epoch id for avg

* chore(dogfood): golangci-lint

* chore(dogfood): lint gosec again

* fix(dogfood): use correct string for pubkey

* fix(dogfood): ++ the undelegation hold count

The line was accidentally deleted.
Comment on lines +90 to +98
for key := range prev {
bz := []byte(key) // undo string operation
var keyObj tmprotocrypto.PublicKey
k.cdc.MustUnmarshal(bz, &keyObj) // undo marshal operation
res = append(res, abci.ValidatorUpdate{
PubKey: keyObj,
Power: 0,
})
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
Comment on lines +698 to +712
for k := range m.List {
v := m.List[k]
baseI := i
i = encodeVarintDogfood(dAtA, i, uint64(v))
i--
dAtA[i] = 0x10
i -= len(k)
copy(dAtA[i:], k)
i = encodeVarintDogfood(dAtA, i, uint64(len(k)))
i--
dAtA[i] = 0xa
i = encodeVarintDogfood(dAtA, i, uint64(baseI-i))
i--
dAtA[i] = 0xa
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
Comment on lines +834 to +839
for k, v := range m.List {
_ = k
_ = v
mapEntrySize := 1 + len(k) + sovDogfood(uint64(len(k))) + 1 + sovDogfood(uint64(v))
n += mapEntrySize + 1 + sovDogfood(uint64(mapEntrySize))
}

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
Copy link
Contributor

@TimmyExogenous TimmyExogenous left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. It looks like the PR has already been reviewed in the old repo.

@TimmyExogenous TimmyExogenous merged commit 6e812e9 into develop Mar 6, 2024
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants